﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericGA.extrandom
{
    /// <summary>
    /// An extended Random class for extra random functions.
    /// </summary>
    public class ExtRandom : Random
    {

        /// <summary>
        /// Constructor which uses a generated seed.
        /// </summary>
        public ExtRandom() : base() { }

        /// <summary>
        /// Constructor which uses a provided seed.
        /// </summary>
        /// <param name="seed">Seed to use.</param>
        public ExtRandom(int seed) : base(seed) { }

        /// <summary>
        /// Byte array to generate random bytes to be used to produce numbers. Stored as a static variable for efficiency.
        /// </summary>
        private static readonly byte[] longByteArray = new byte[8];

        /// <summary>
        /// Generate a random boolean with a bias towards true values between 0.0 and 1.0.
        /// </summary>
        /// <param name="biasOnTrue">The bias between 0.0 and 1.0 where 0.0 never returns true, always false, 1.0 always returns true, never false, and 0.5 returns an equal distribution of true and false.</param>
        /// <returns>A biased random boolean value.</returns>
        public bool NextBool(double biasOnTrue)
        {
            return NextDouble() >= biasOnTrue;
        }

        /// <summary>
        /// Default parameter of NextBool with biasOnTrue of 0.5 making a uniform probability.
        /// </summary>
        /// <returns>An unbiased random boolean value.</returns>
        public bool NextBool()
        {
            return NextBool(0.5);
        }

        /// <summary>
        /// Return a random 64 bit unsigned integer.
        /// </summary>
        /// <returns>A random long value.</returns>
        public ulong NextULong()
        {
            NextBytes(longByteArray);
            return BitConverter.ToUInt64(longByteArray, 0);
        }

        /// <summary>
        /// Create a bit mask array with bits randomly set.
        /// </summary>
        /// <param name="bitSetProbability">The probability of each bit being set.</param>
        /// <param name="bytes">The array to be filled with masks.</param>
        public void NextBytesMask(double bitSetProbability, byte[] bytes)
        {
            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = 0;
                for (int b = 0; b < 8; b++)
                {
                    if (NextBool(bitSetProbability))
                    {
                        bytes[i] = (byte)(bytes[i] | 1);
                    }
                    bytes[i] = (byte)(bytes[i] << 1);
                }
            }
        }

        /// <summary>
        /// Default parameter of NextBytesMask with bitSetProbability of 0.5 making a uniform probability.
        /// </summary>
        /// <param name="bytes">The array to be filled with masks.</param>
        public void NextBytesMask(byte[] bytes)
        {
            NextBytesMask(0.5, bytes);
        }

        /// <summary>
        /// Return a 64 bit mask with bits randomly set.
        /// </summary>
        /// <param name="bitSetProbability">The probability of each bit being set.</param>
        /// <returns>A random bit mask.</returns>
        public ulong NextULongMask(double bitSetProbability)
        {
            NextBytesMask(bitSetProbability, longByteArray);
            return BitConverter.ToUInt64(longByteArray, 0);
        }

        /// <summary>
        /// Default parameter of NextULongMask with bitSetProbability of 0.5 making a uniform probability.
        /// </summary>
        /// <returns>A random bit mask.</returns>
        public ulong NextULongMask()
        {
            return NextULongMask(0.5);
        }

    }
}
